package com.example.wanbaqian.widget;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.widget.EditText;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义文件输入框，自带文件清除按钮bystart
 */
public class CustomEditText extends AppCompatEditText {

	/**
	 * 右侧图片控件
	 */
	private Drawable rightDrawable,leftDrawable;

	public CustomEditText(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public CustomEditText(Context context) {
		super(context);
		init();
	}

	public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}
	public static final String DEFAULT_MENTION_PATTERN = "#[\\u4e00-\\u9fa5\\w\\-]+#";
	private void init() {
		mRangeArrayList = new ArrayList<>();
		mMentionTextColor = Color.YELLOW;
		mPattern = Pattern.compile(DEFAULT_MENTION_PATTERN);
		setDrawable();
		// 增加文本监听器
		addTextChangedListener(new TextWatcher() {

			@Override
			public void onTextChanged(CharSequence s, int start, int before,
									  int count) {
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
										  int after) {
			}

			@Override
			public void afterTextChanged(Editable s) {
				setDrawable();
			}
		});
	}


	private void setDrawable() {
		if (length() == 0) {
			setCompoundDrawables(leftDrawable, null, null, null);
		} else {
			setCompoundDrawables(leftDrawable, null, rightDrawable, null);
		}
	}

	@Override
	public void setCompoundDrawables(Drawable left, Drawable top,
									 Drawable right, Drawable bottom) {
		if (rightDrawable == null) {
			rightDrawable = right;
		}
		if(leftDrawable==null){
			leftDrawable=left;
		}
		super.setCompoundDrawables(left, top, right, bottom);
	}

	/**
	 * 输入事件处理
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if(this.isEnabled()){
			if (rightDrawable != null && event.getAction() == MotionEvent.ACTION_UP) {
				int eventX = (int) event.getX();
				int width = getWidth();
				int right1 = getTotalPaddingRight();
				int right2 = getPaddingRight();
				if (eventX > (width - right1) && eventX < width - right2) {
					setText("");
					event.setAction(MotionEvent.ACTION_CANCEL);
				}
			}
		}
		return super.onTouchEvent(event);
	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		this.rightDrawable = null;
	}


	private Pattern mPattern;
	private int mMentionTextColor;
	private Runnable mAction;
	private boolean mIsSelected;
	private Range mLastSelectedRange;
	private List<Range> mRangeArrayList;

	@Override
	public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
		return new HackInputConnection(super.onCreateInputConnection(outAttrs), true, this);
	}

	@Override
	public void setText(final CharSequence text, BufferType type) {
		super.setText(text, type);
		//hack, put the cursor at the end of text after calling setText() method
		if (mAction == null) {
			mAction = new Runnable() {
				@Override
				public void run() {
					setSelection(getText().length());
				}
			};
		}
		post(mAction);
	}

	@Override
	protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
		colorMentionString();
	}
	@Override
	protected void onSelectionChanged(int selStart, int selEnd) {
		super.onSelectionChanged(selStart, selEnd);
		//avoid infinite recursion after calling setSelection()
		if (mLastSelectedRange != null && mLastSelectedRange.isEqual(selStart, selEnd)) {
			return;
		}

		//if user cancel a selection of mention string, reset the state of 'mIsSelected'
		Range closestRange = getRangeOfClosestMentionString(selStart, selEnd);
		if (closestRange != null && closestRange.to == selEnd) {
			mIsSelected = false;
		}

		Range nearbyRange = getRangeOfNearbyMentionString(selStart, selEnd);
		//if there is no mention string nearby the cursor, just skip
		if (nearbyRange == null) {
			return;
		}

		//forbid cursor located in the mention string.
		if (selStart == selEnd) {
			setSelection(nearbyRange.getAnchorPosition(selStart));
		} else {
			if (selEnd < nearbyRange.to) {
				setSelection(selStart, nearbyRange.to);
			}
			if (selStart > nearbyRange.from) {
				setSelection(nearbyRange.from, selEnd);
			}
		}
	}

	private void colorMentionString() {
		//reset state
		mIsSelected = false;
		if (mRangeArrayList != null) {
			mRangeArrayList.clear();
		}

		Editable spannableText = getText();
		if (spannableText == null || TextUtils.isEmpty(spannableText.toString())) {
			return;
		}

		//remove previous spans
		ForegroundColorSpan[] oldSpans = spannableText.getSpans(0, spannableText.length(), ForegroundColorSpan.class);
		for (ForegroundColorSpan oldSpan : oldSpans) {
			spannableText.removeSpan(oldSpan);
		}

		//find mention string and color it
		int lastMentionIndex = -1;
		String text = spannableText.toString();
		Matcher matcher = mPattern.matcher(text);
		while (matcher.find()) {
			String mentionText = matcher.group();
			int start;
			if (lastMentionIndex != -1) {
				start = text.indexOf(mentionText, lastMentionIndex);
			} else {
				start = text.indexOf(mentionText);
			}
			int end = start + mentionText.length();
			if(start!=0)
				return;
			spannableText.setSpan(new ForegroundColorSpan(mMentionTextColor), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
			lastMentionIndex = end;
			//record all mention-string's position
			if(mRangeArrayList.size()<=charsize)
				mRangeArrayList.add(new Range(start, end));
		}
	}
	private Range getRangeOfNearbyMentionString(int selStart, int selEnd) {
		if (mRangeArrayList == null) {
			return null;
		}
		for (Range range : mRangeArrayList) {
			if (range.isWrappedBy(selStart, selEnd)) {
				return range;
			}
		}
		return null;
	}

	private Range getRangeOfClosestMentionString(int selStart, int selEnd) {
		if (mRangeArrayList == null) {
			return null;
		}
		for (Range range : mRangeArrayList) {
			if (range.contains(selStart, selEnd)) {
				return range;
			}
		}
		return null;
	}

	private class HackInputConnection extends InputConnectionWrapper {
		private EditText editText;

		public HackInputConnection(InputConnection target, boolean mutable, CustomEditText editText) {
			super(target, mutable);
			this.editText = editText;
		}

		@Override
		public boolean sendKeyEvent(KeyEvent event) {
			if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
				int selectionStart = editText.getSelectionStart();
				int selectionEnd = editText.getSelectionEnd();
				Range closestRange = getRangeOfClosestMentionString(selectionStart, selectionEnd);
				if (closestRange == null) {
					mIsSelected = false;
					return super.sendKeyEvent(event);
				}
				//if mention string has been selected or the cursor is at the beginning of mention string, just use default action(delete)
				if (mIsSelected || selectionStart == closestRange.from) {
					mIsSelected = false;
					return super.sendKeyEvent(event);
				} else {
					//select the mention string
					mIsSelected = true;
					mLastSelectedRange = closestRange;
					setSelection(closestRange.to, closestRange.from);
				}
				return true;
			}
			return super.sendKeyEvent(event);
		}

		@Override
		public boolean deleteSurroundingText(int beforeLength, int afterLength) {
			if (beforeLength == 1 && afterLength == 0) {
				return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
						&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
			}
			return super.deleteSurroundingText(beforeLength, afterLength);
		}
	}
	//helper class to record the position of mention string in EditText
	private class Range {
		int from;
		int to;

		public Range(int from, int to) {
			this.from = from;
			this.to = to;
		}

		public boolean isWrappedBy(int start, int end) {
			return (start > from && start < to) || (end > from && end < to);
		}

		public boolean contains(int start, int end) {
			return from <= start && to >= end;
		}

		public boolean isEqual(int start, int end) {
			return (from == start && to == end) || (from == end && to == start);
		}

		public int getAnchorPosition(int value) {
			if ((value - from) - (to - value) >= 0) {
				return to;
			} else {
				return from;
			}
		}
	}

	int charsize=0;
	public void setCharSize(int i)
	{
		charsize=1;
	}
	public String getChar(){
		if(mRangeArrayList.size()==0)
			return "";
		String chars=getText().toString().substring(0, mRangeArrayList.get(0).to);
		return chars.substring(1,chars.length()-1);
	}
	public String getString(){
		if(mRangeArrayList.size()==0)
			return getText().toString();
		String chars=getText().toString().substring( mRangeArrayList.get(0).to);
		return chars;//.substring(1,chars.length()-1)
	}
}
